/*******************************************************************************
 * Copyright IBM Corp. and others 2000
 *
 * This program and the accompanying materials are made available under
 * the terms of the Eclipse Public License 2.0 which accompanies this
 * distribution and is available at https://www.eclipse.org/legal/epl-2.0/
 * or the Apache License, Version 2.0 which accompanies this distribution
 * and is available at https://www.apache.org/licenses/LICENSE-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License, v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception [1] and GNU General Public
 * License, version 2 with the OpenJDK Assembly Exception [2].
 *
 * [1] https://www.gnu.org/software/classpath/license.html
 * [2] https://openjdk.org/legal/assembly-exception.html
 *
 * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 OR GPL-2.0-only WITH OpenJDK-assembly-exception-1.0
 *******************************************************************************/

#include "infra/Random.hpp"

#include <limits.h>
#include <stdint.h>
#include "compile/Compilation.hpp"
#include "ras/Logger.hpp"

TR_HasRandomGenerator::TR_HasRandomGenerator(TR::Compilation *comp)
    : _randomGenerator(comp->primaryRandom())
{}

uint32_t TR_RandomGenerator::getRandom()
{
    // Linear congruential parameters used by GCC; see https://en.wikipedia.org/wiki/Linear_congruential_generator
    // ...except we keep the sign bit (bit 31) while apparently they do not.
    //
    _bits = 1103515245 * _bits + 12345;

    // _bits alternates between odd and even, which is terrible for randomBoolean.
    // Let's mix it up a bit.
    //
    return (uint32_t)(_bits ^ (_bits >> 16));
}

int32_t TR_RandomGenerator::getRandom(int32_t min, int32_t max)
{
    uint32_t bits = getRandom();
    uint32_t range = max - min + 1;
    if (range == 0) // corner case when range is INT_MIN..INT_MAX
        return (int32_t)bits;
    uint32_t rand = bits % range; // unsigned remainder
    return min + rand;
}

void TR_RandomGenerator::setSeed(uint32_t newSeed)
{
    _bits = newSeed;

    // Similar seeds can produce sequences that start off similar, so eat a few.
    //
    for (int i = 0; i < 5; i++)
        getRandom();
}

class RandomExercizer : public TR_HasRandomGenerator {
    TR::Compilation *_comp;

public:
    RandomExercizer(TR::Compilation *comp)
        : TR_HasRandomGenerator(comp)
        , _comp(comp)
    {}

    TR::Compilation *comp() { return _comp; }
};

void TR_RandomGenerator::exercise(int32_t period, TR::Compilation *comp)
{
    // Don't use the actual random generator because we don't want to perturb it
    RandomExercizer ex(comp);

    OMR::Logger *log = comp->log();
    bool trace = comp->getOption(TR_TraceAll);

    logprintf(trace, log, "  %12s %12s %12s %12s %12s %12s\n", "Int", "Int(-5,5)", "Int(1,1)", "Int(MIN,MAX)",
        "Boolean", "Boolean(5)");

    for (int32_t i = 0; i < period; i++) {
        logprintf(trace, log, "  %12d %12d %12d %12d %12d %12d\n", ex.randomInt(), ex.randomInt(-5, 5),
            ex.randomInt(1, 1), ex.randomInt(INT_MIN, INT_MAX), ex.randomBoolean(), ex.randomBoolean(5));
    }
}
